#include "common_Socket.h"

#include <arpa/inet.h>
#include <cstdlib>
#include <netdb.h>
#include <sstream>
#include <string>

Socket::Socket() {
	socketFD = socket(AF_INET, SOCK_STREAM, 0);
}

Socket::Socket(int socketFD) {
	this->socketFD = socketFD;
}

Socket::~Socket() { }

bool Socket::conectar(std::string ip, int puerto) {
	struct hostent* hostEntity;
	struct sockaddr_in server_address;
	struct in_addr ipIn;

	inet_pton(AF_INET, ip.c_str(), &ipIn);
	if ((hostEntity = gethostbyaddr(&ipIn, sizeof(ipIn), AF_INET)) == NULL) {
		return false;
	}

	server_address.sin_family = AF_INET;
	server_address.sin_port = htons(puerto);
	server_address.sin_addr = *((struct in_addr*) hostEntity->h_addr);
	memset(&(server_address.sin_zero), 8, sizeof(int));

	if (connect(socketFD, (struct sockaddr*) &server_address, 
				sizeof(struct sockaddr)) == -1) {
		return false;
	}

	return true;
}

bool Socket::enlazar(int puerto) {
	int opt = 1;
	struct sockaddr_in server_address;

	memset((char*) &server_address, 0, sizeof(server_address));
	server_address.sin_family = AF_INET;
	server_address.sin_port = htons(puerto);
	server_address.sin_addr.s_addr = INADDR_ANY;

	if (setsockopt(socketFD, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt))) {
		return false;
	}
	
	if (bind(socketFD, (struct sockaddr*) &server_address, 
			 (socklen_t) sizeof(struct sockaddr)) < 0) {
		return false;
	}

	return true;
}

int Socket::escuchar() {
	return listen(socketFD, 10);
}

int Socket::aceptar() {
	struct sockaddr_in client_address;
	unsigned int clilen = (socklen_t) sizeof(client_address);

	return accept(socketFD, (struct sockaddr*) &client_address, &clilen);
}

void Socket::enviar(std::string mensaje) {
	uint32_t total = htonl(mensaje.length());
	uint32_t enviado = 0;

	if (!send(socketFD, &total, 4, 0) == 4) {
		return;
	}

	uint32_t totalAEnviar = mensaje.length();

	uint32_t r = send(socketFD, mensaje.c_str(), totalAEnviar, 0);
	enviado += r;

	while (enviado < totalAEnviar) {
		mensaje = mensaje.substr(r);

		r = send(socketFD, mensaje.c_str(), totalAEnviar - enviado, 0);
		enviado += r;
	}
}

int Socket::recibir(std::string &respuesta) {
	char buffer[MAX_LENGTH + 1];

	// Entero de 4 bytes
	uint32_t totalARecibir = 4;
	uint32_t totalRecibido = 0;

	// Recibo la cantidad de bytes
	if (recv(socketFD, (void*) &totalARecibir, 4, 0) != 4) {
		return -1;
	}

	totalARecibir = ntohl(totalARecibir);

	// Recibo el resto del mensaje
	while (totalRecibido < totalARecibir) {
		memset(buffer, 0, MAX_LENGTH);

		int r = recv(socketFD, buffer, MAX_LENGTH, 0);

		buffer[MAX_LENGTH] = '\0';

		if (r == 0 || r < 0) {
			break;
		}

		totalRecibido += r;
		respuesta.append(buffer);
	}

	return totalRecibido;
}

bool Socket::desconectar() {
	return shutdown(socketFD, SHUT_RDWR) >= 0;
}

int Socket::cerrar() {
	return close(socketFD);
}
